package festrung

import (
	"fmt"
	"sync"
	"time"

	"starsine.cn/appTpl/cmn"
	"starsine.cn/appTpl/cmn/config/aconf"
	"starsine.cn/appTpl/cmn/logs"
	"starsine.cn/appTpl/cmn/timermgr"
	"starsine.cn/appTpl/svclog/src/fe/ws-server/wsserver"
)

type StrungMng struct {
	strungSize int
	data       map[string]*Strung

	tmID int64
	tm   *timermgr.TimerMgr

	sync.RWMutex
}

func CreateStrungMng() *StrungMng {
	p := new(StrungMng)

	p.strungSize = aconf.GetInt("SvcLog.Strung.Size", 100, aconf.WithENV("svclog_strung_size"))
	p.data = make(map[string]*Strung)

	p.tmID = -1
	p.tm = cmn.GetTm()

	return p
}

func (p *StrungMng) Dump() (str string) {
	p.RWMutex.RLock()
	defer p.RWMutex.RUnlock()

	str += fmt.Sprintln("Fe-StrungMng:")
	for k, v := range p.data {
		str += fmt.Sprintf("instanceID: %s, Strung: %s\n", k, v.String())
	}

	return str
}

func (p *StrungMng) Start() {
	p.checkPeriod()
}

func (p *StrungMng) End() {
	p.tm.CancelTimer(p.tmID)
	p.tmID = -1
}

func (p *StrungMng) checkAndRecycle(params interface{}) {
	p.Recycle()
}

func (p *StrungMng) checkPeriod() {
	p.tmID = p.tm.AddPeriodTimer(
		time.Duration(10),
		timermgr.NewOnTimeOut(p.checkAndRecycle,
			nil,
		))
}

func (p *StrungMng) Create(k string, conn *wsserver.WsServer) *Strung {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	if v, ok := p.data[k]; ok {
		v.AddConn(conn)
		logs.Debugf("Fe-StrungMng Create(%s) to add conn, return Strung(%p)", k, v)
		return v
	}

	v := CreateStrung(p.strungSize)
	v.AddConn(conn)
	v.Start()
	p.data[k] = v

	logs.Debugf("Fe-StrungMng Create(%s) to create Strung(%p)", k, v)
	return v
}

func (p *StrungMng) Get(k string) *Strung {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	if v, ok := p.data[k]; ok {
		return v
	} else {
		return nil
	}
}

func (p *StrungMng) close(k string) {
	if v, ok := p.data[k]; ok {
		v.End()
		delete(p.data, k)
		logs.Debugf("Fe-StrungMng close(%s)", k)
	} else {
		logs.Errorf("Deleting an unexist Fe-Strung(%s)", k)
	}
}

func (p *StrungMng) Close(k string) {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	p.close(k)
}

func (p *StrungMng) CloseConn(k string, conn *wsserver.WsServer) {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	if v, ok := p.data[k]; ok {
		v.DelConn(conn)
		logs.Debugf("Fe-StrungMng CloseConn(%s, %p)", k, conn)
		if len(v.conn) == 0 {
			p.close(k)
		}
	} else {
		logs.Errorf("Deleting an unexist Fe-Strung(%s)", k)
	}
}

func (p *StrungMng) Recycle() {
	for k, v := range p.data {
		if len(v.conn) == 0 {
			p.Close(k)
		} else {
			for _, c := range v.conn {
				if c.IsExpired() {
					v.DelConn(c.conn)
					if len(v.conn) == 0 {
						p.Close(k)
					}
				}
			}
		}
	}
}
